package Scheme101;

import java.util.List;



public class StandardScheme {

    public Cell not(List<Cell> cells) {
        if (cells.size() != 2) {
            System.out.println("not: Usage [not boolean]");
            System.exit(1);
        }
        if (cells.get(1).value.equals("#t")) {
            return new Cell("Symbol", "#f");
        } else if (cells.get(1).value.equals("#f")) {
            return new Cell("Symbol", "#t");
        } else {
            System.out.println("not: Can only not a boolean");
            System.exit(1);
        }
        return null;
    }

    public Cell equal(List<Cell> cells) {
        if (cells.size() != 3) {
            System.out.println("=: Usage [= a b]");
            System.exit(1);
        }
        if (cells.get(1).value.equals(cells.get(2).value)) {
            return new Cell("Symbol", "#t");
        } else {
            return new Cell("Symbol", "#f");
        }
    }

    public Cell greater(List<Cell> cells) {
        if (cells.size() != 3) {
            System.out.println(">: Usage [> a b]");
            System.exit(1);
        }
        if (cells.get(1).type.equals("Number") || cells.get(1).type.equals("Symbol")) {
            if (cells.get(1).type.equals(cells.get(2).type)) {
                try {
                    if (Double.parseDouble(cells.get(1).value) > Double.parseDouble(cells.get(2).value)) {
                        return new Cell("Symbol", "#t");
                    }
                    return new Cell("Symbol", "#f");
                } catch (Exception e) {
                    if (cells.get(1).value.compareTo(cells.get(2).value) > 0) {
                        return new Cell("Symbol", "#t");
                    }
                    return new Cell("Symbol", "#f");

                }
            } else {
                System.out.println(">: greater can only be applied parameters of the same type");
                System.exit(1);
            }
        }
        System.out.println(">: greater can only be applied to symbols and numbers");
        System.exit(1);
        return null;
    }

    public Cell less(List<Cell> cells) {
        if (cells.size() != 3) {
            System.out.println("<: Usage [< a b]");
            System.exit(1);
        }
        if (cells.get(1).type.equals("Number") || cells.get(1).type.equals("Symbol")) {
            if (cells.get(1).type.equals(cells.get(2).type)) {
                try {
                    if (Double.parseDouble(cells.get(1).value) < Double.parseDouble(cells.get(2).value)) {
                        return new Cell("Symbol", "#t");
                    }
                    return new Cell("Symbol", "#f");
                } catch (Exception e) {
                    if (cells.get(1).value.compareTo(cells.get(2).value) < 0) {
                        return new Cell("Symbol", "#t");
                    }
                    return new Cell("Symbol", "#f");

                }
            } else {
                System.out.println("<: less can only be applied parameters of the same type");
                System.exit(1);
            }
        }
        System.out.println("<: less can only be applied to symbols and numbers");
        System.exit(1);
        return null;
    }

    public Cell greaterEqual(List<Cell> cells) {
        if (cells.size() != 3) {
            System.out.println(">=: Usage [>= a b]");
            System.exit(1);
        }
        if (cells.get(1).type.equals("Number") || cells.get(1).type.equals("Symbol")) {
            if (cells.get(1).type.equals(cells.get(2).type)) {
                try {
                    if (Double.parseDouble(cells.get(1).value) >= Double.parseDouble(cells.get(2).value)) {
                        return new Cell("Symbol", "#t");
                    }
                    return new Cell("Symbol", "#f");
                } catch (Exception e) {
                    if (cells.get(1).value.compareTo(cells.get(2).value) >= 0) {
                        return new Cell("Symbol", "#t");
                    }
                    return new Cell("Symbol", "#f");

                }
            } else {
                System.out.println(">=: greater then or equal can only be applied parameters of the same type");
                System.exit(1);
            }
        }
        System.out.println(">=: greater then or equal can only be applied to symbols and numbers");
        System.exit(1);
        return null;
    }

    public Cell lessEqual(List<Cell> cells) {
        if (cells.size() != 3) {
            System.out.println("<=: Usage [<= a b]");
            System.exit(1);
        }
        if (cells.get(1).type.equals("Number") || cells.get(1).type.equals("Symbol")) {
            if (cells.get(1).type.equals(cells.get(2).type)) {
                try {
                    if (Double.parseDouble(cells.get(1).value) <= Double.parseDouble(cells.get(2).value)) {
                        return new Cell("Symbol", "#t");
                    }
                    return new Cell("Symbol", "#f");
                } catch (Exception e) {
                    if (cells.get(1).value.compareTo(cells.get(2).value) <= 0) {
                        return new Cell("Symbol", "#t");
                    }
                    return new Cell("Symbol", "#f");

                }
            } else {
                System.out.println("<=: less then or equal can only be applied parameters of the same type");
                System.exit(1);
            }
        }
        System.out.println("<=: less then or equal can only be applied to symbols and numbers");
        System.exit(1);
        return null;
    }

    public Cell add(List<Cell> cells) {
        int res = 0;
        for (int i = 1; i < cells.size(); i++) {
            if (!cells.get(i).type.equals("Number")) {
                System.out.println("+: addition can only be applied to numbers");
                System.exit(1);
            }
            res += Integer.parseInt(cells.get(i).value);
        }

        return new Cell("Number", "" + res);
    }

    public Cell mul(List<Cell> cells) {
        int res = 1;
        for (int i = 1; i < cells.size(); i++) {
            if (!cells.get(i).type.equals("Number")) {
                System.out.println("*: multiplication can only be applied to numbers");
                System.exit(1);
            }
            res *= Integer.parseInt(cells.get(i).value);
        }

        return new Cell("Number", "" + res);
    }

    public Cell sub(List<Cell> cells) {
        int res = 0;

        if (cells.size() == 1) {
            System.out.println("-: Usage [- a ...]");
            System.exit(1);
        }
        if (cells.get(1).type.equals("Number")) {
            res = Integer.parseInt(cells.get(1).value);
            for (int i = 2; i < cells.size(); i++) {
                if (!cells.get(i).type.equals("Number")) {
                    System.out.println("-: subtraction can only be applied to numbers");
                    System.exit(1);
                }
                res -= Integer.parseInt(cells.get(i).value);
            }
        } else {
            System.out.println("-: subtraction can only be applied to numbers");
            System.exit(1);
        }
        return new Cell("Number", "" + res);
    }

    public Cell div(List<Cell> cells) {
        double res = 0;

        if (cells.size() == 1) {
            System.out.println("/: Usage [/ a ...]");
            System.exit(1);
        }
        if (cells.get(1).type.equals("Number")) {
            res = Double.parseDouble(cells.get(1).value);
            for (int i = 2; i < cells.size(); i++) {
                if (!cells.get(i).type.equals("Number")) {
                    System.out.println("/: division can only be applied to numbers");
                    System.exit(1);
                }
                res /= Double.parseDouble(cells.get(i).value);
            }
        } else {
            System.out.println("/: division can only be applied to numbers");
            System.exit(1);
        }
        return new Cell("Number", "" + res);
    }

    public Cell intDiv(List<Cell> cells) {
        int res = 0;

        if (cells.size() == 1) {
            System.out.println("div: Usage [div a ...]");
            System.exit(1);
        }
        if (cells.get(1).type.equals("Number")) {
            res = Integer.parseInt(cells.get(1).value);
            for (int i = 2; i < cells.size(); i++) {
                if (!cells.get(i).type.equals("Number")) {
                    System.out.println("div: integer division can only be applied to numbers");
                    System.exit(1);
                }
                res /= Integer.parseInt(cells.get(i).value);
            }
        } else {
            System.out.println("div: integer division can only be applied to numbers");
            System.exit(1);
        }
        return new Cell("Number", "" + res);
    }

    public Cell len(List<Cell> cells) {
        if (cells.size() != 2) {
            System.out.println("len: Usage [len list]");
            System.exit(1);
        }
        if (cells.get(1).type.equals("List")) {
            return new Cell("Number", ""+cells.get(1).list.size());
        } else {
            System.out.println("len: Can only take the length of a list");
            System.exit(1);
        }
        return null;
    }

    public Cell cons(List<Cell> cells) {
        Cell nc;
        if (cells.size() != 3) {
            System.out.println("cons: Usage [cons symbol list]");
            System.exit(1);
        }
        if (cells.get(1).type.equals("Symbol") && cells.get(2).type.equals("List")) {
            nc = new Cell("List");
            nc.list.add(cells.get(1));
            nc.list.addAll(cells.get(2).list);
            return nc;
        } else {
            System.out.println("cons: Usage [cons symbol list]");
            System.exit(1);
        }
        return null;
    }

    public Cell first(List<Cell> cells) {
        if (cells.size() != 2) {
            System.out.println("first: Usage [first list]");
            System.exit(1);
        }
        if (cells.get(1).type.equals("List") && cells.get(1).list.size() > 0) {
            return cells.get(1).list.get(0);
        } else {
            System.out.println("first: Can only get the first element of a nonempty list");
            System.exit(1);
        }
        return null;
    }

    public Cell rest(List<Cell> cells) {
        Cell nc;
        if (cells.size() != 2) {
            System.out.println("rest: Usage [rest list]");
            System.exit(1);
        }
        if (cells.get(1).type.equals("List") && cells.get(1).list.size() > 0) {
            nc = new Cell("List");
            nc.list.addAll(cells.get(1).list);
            nc.list.remove(0);
            return nc;
        } else {
            System.out.println("rest: Can only get the rest of a nonempty list");
            System.exit(1);
        }
        return null;
    }

    public Cell append(List<Cell> cells) {
        Cell nc = new Cell("List");
        for (int i = 1; i < cells.size(); i++) {
            if (!cells.get(i).type.equals("List")) {
                System.out.println("append: append can only be applied to lists");
                System.exit(1);
            }
            nc.list.addAll(cells.get(i).list);
        }

        return nc;
    }

    public Cell list(List<Cell> cells) {
        Cell nc = new Cell("List");
        for (int i = 1; i < cells.size(); i++) {
            nc.list.add(cells.get(i));
        }

        return nc;
    }

    public Cell isList(List<Cell> cells) {
        if (cells.size() != 2) {
            System.out.println("list?: Usage [list? var]");
            System.exit(1);
        }
        if (cells.get(1).type.equals("List"))
            return new Cell("Symbol", "#t");
        else
            return new Cell("Symbol", "#f");
    }

    public Cell isEmpty(List<Cell> cells) {
        if (cells.size() != 2) {
            System.out.println("null?: Usage [null? var]");
            System.exit(1);
        }
        if (cells.get(1).type.equals("List") && cells.get(1).list.size() == 0)
            return new Cell("Symbol", "#t");
        else
            return new Cell("Symbol", "#f");
    }

    public Cell isSymbol(List<Cell> cells) {
        if (cells.size() != 2) {
            System.out.println("symbol?: Usage [symbol? var]");
            System.exit(1);
        }
        if (cells.get(1).type.equals("Symbol"))
            return new Cell("Symbol", "#t");
        else
            return new Cell("Symbol", "#f");
    }

    public Cell isNumber(List<Cell> cells) {
        if (cells.size() != 2) {
            System.out.println("number?: Usage [number? var]");
            System.exit(1);
        }
        if (cells.get(1).type.equals("Number"))
            return new Cell("Symbol", "#t");
        else
            return new Cell("Symbol", "#f");
    }
}
